﻿#include "infer_seg_v2.h"
#include "common\common.hpp"
#include <assert.h>
#include "../public/dlm_crypt.hpp"
#include "../public/gpu_utility.hpp"
#include "../public/cuda_utility.hpp"
#include "clipper.h"

namespace txr_algo_dlm_seg
{ 

//===================================================================================
InferSegV2::InferSegV2()
{
}
InferSegV2::~InferSegV2()
{
}

bool InferSegV2::Init(int gpu_id, const char* dats_path)
{
	if (!InferBase::Init(gpu_id,dats_path))
	{
		return false;
	}
	//......
	return true;
}

bool InferSegV2::LoadEngine(std::vector<char> v_engine_data)
{
	if (!InferBase::LoadEngine(v_engine_data))
	{
		return false;
	}
	if (m_engine->getNbBindings() != 2)
	{
		return true;
	}
	assert(m_engine->getNbBindings() == 2);
	m_v_dev_buffer.resize(2);
	m_v_dev_buf_size.resize(2);
	//input img_data
	m_v_dev_buf_size[0] = 
		m_info.batch_size * m_info.input_channel *
		m_info.img_max_len * m_info.img_max_len * sizeof(float);
	//output img_mask
	m_v_dev_buf_size[1] =
		m_info.batch_size * 
		m_info.img_max_len * m_info.img_max_len * sizeof(float);
	
	for (int i = 0; i < m_v_dev_buffer.size(); ++i)
	{
		cudaMalloc(&m_v_dev_buffer[i], m_v_dev_buf_size[i]);
	}

	return true;
}

std::tuple<std::vector<float>, int, int> InferSegV2::prepareImage(st_detect_unit* p_units, int num, std::vector<cv::Mat>& v_imgs) 
{

	std::vector<float> result;
	v_imgs.resize(num);
	float ratio = 1.0f;
	int final_resize_h = 0;
	int final_resize_w = 0;
	for (int n = 0; n < num; ++n)
	{
		st_img_rgb& img = p_units[n].img;
		cv::Mat& src_img = v_imgs[n];
		{
			src_img.create(img.h, img.w, CV_8UC3);
			int index = 0;
			for (int i = 0; i < src_img.rows; ++i)
			{
				for (int j = 0; j < src_img.cols; ++j)
				{
					cv::Vec3b rgb;
					rgb[2] = img.pp_rgbf[0][index]*255.f;
					rgb[1] = img.pp_rgbf[1][index]*255.f;
					rgb[0] = img.pp_rgbf[2][index]*255.f;
					src_img.at<cv::Vec3b>(i, j) = rgb;
					index++;
				}
			}
		}
		cv::Mat rsz_img;

		if (__max(float(src_img.rows), float(src_img.cols)) > m_info.img_max_len) {
			if (float(src_img.rows) > float(src_img.cols))
				ratio = float(m_info.img_max_len) / float(src_img.rows);
			else
				ratio = float(m_info.img_max_len) / float(src_img.cols);
		}
		else {
			ratio = 1.0f;
		}

		int resize_h = int(float(src_img.rows) * ratio);
		int resize_w = int(float(src_img.cols) * ratio);

		resize_h = __max(int(std::round(float(resize_h) / 32) * 32), 32);
		resize_w = __max(int(std::round(float(resize_w) / 32) * 32), 32);


		cv::resize(src_img, rsz_img, cv::Size(resize_w, resize_h));

		rsz_img.convertTo(rsz_img, CV_32FC3, 1.0 / 255.f, 0);


		std::vector<cv::Mat> bgr_channels(3);
		cv::split(rsz_img, bgr_channels);


		for (int i = 0; i < 3; ++i) {
			bgr_channels[i] = (bgr_channels[i] - m_info.img_mean[i]) / m_info.img_std[i];

			const float* ptr = bgr_channels[i].ptr<float>(0);
			size_t channel_size = bgr_channels[i].total();
			result.resize(result.size() + channel_size);
			std::memcpy(result.data() + result.size() - channel_size, ptr, channel_size * sizeof(float));
		}


		//cv::imshow("flt_img", rsz_img);
		//cv::waitKey(0);
		//cv::imwrite("preprocess.png", flt_img);


		// 存储最终的 resize_h 和 resize_w
		final_resize_h = resize_h;
		final_resize_w = resize_w;

	}
	return std::make_tuple(result, final_resize_h, final_resize_w);
}

/*
std::tuple<std::vector<float>, int, int> InferSegV2::prepareImage(st_detect_unit* p_units, int num, std::vector<cv::Mat>& v_imgs) {

	std::vector<float> result;
	result.resize(m_info.img_max_len*m_info.img_max_len*3);
	v_imgs.resize(num);

	float ratio = 1.0f;
	int final_resize_h = 0;
	int final_resize_w = 0;
	for (int i = 0; i < num; ++i)
	{
		st_img_rgb& img = p_units[i].img;
 		cv::Mat & src_img = v_imgs[i];
		{
			src_img.create(img.h, img.w, CV_32FC3);
			int index = 0;
			for (int i = 0; i < src_img.rows; ++i)
			{
				for (int j = 0; j < src_img.cols; ++j)
				{
					cv::Vec3f rgb;
					rgb[2] = (img.pp_rgbf[0][index] - m_info.img_mean[2]) / m_info.img_std[2];
					rgb[1] = (img.pp_rgbf[1][index] - m_info.img_mean[1]) / m_info.img_std[1];
					rgb[0] = (img.pp_rgbf[2][index] - m_info.img_mean[0]) / m_info.img_std[0];
					src_img.at<cv::Vec3f>(i, j) = rgb;
					index++;
				}
			}
		}

// 		{
// 			src_img.create(img.h, img.w, CV_32FC3);
// 			float* src_ptr = src_img.ptr<float>(); // Pointer to the beginning of the data
// 			int index = 0;
// 			// Copy image data directly to src_img
// 			for (int i = 0; i < img.h; ++i) {
// 				for (int j = 0; j < img.w; ++j) {
// 					src_ptr[3 * index] = img.pp_rgbf[2][index]; // Blue channel
// 					src_ptr[3 * index + 1] = img.pp_rgbf[1][index]; // Green channel
// 					src_ptr[3 * index + 2] = img.pp_rgbf[0][index]; // Red channel
// 					index++;
// 				}
// 			}
// 		}
		if (__max(float(src_img.rows), float(src_img.cols)) > m_info.img_max_len) {
			if (float(src_img.rows) > float(src_img.cols))
				ratio = float(m_info.img_max_len) / float(src_img.rows);
			else
				ratio = float(m_info.img_max_len) / float(src_img.cols);
		}
		else {
			ratio = 1.0f;
		}

		int resize_h = int(float(src_img.rows) * ratio);
		int resize_w = int(float(src_img.cols) * ratio);

		resize_h = __max(int(std::round(float(resize_h) / 32) * 32), 32);
		resize_w = __max(int(std::round(float(resize_w) / 32) * 32), 32);

		// Resize image
		cv::Mat rsz_img;
		cv::resize(src_img, rsz_img, cv::Size(resize_w, resize_h));
		//src_img.create(resize_h, resize_w, CV_32FC3);

		//cv::imshow("flt_img", flt_img);
		//cv::waitKey(0);
		{
			int index = 0;
			for (int c = 0; c < rsz_img.channels(); ++c)
			{
				for (int i = 0; i < rsz_img.rows; ++i)
				{
					for (int j = 0; j < rsz_img.cols; ++j)
					{
						result[index] = rsz_img.at<cv::Vec3f>(i, j)[c];
						index++;
					}
				}
			}
		}
		final_resize_h = resize_h;
		final_resize_w = resize_w;
	}
	return std::make_tuple(result, final_resize_h, final_resize_w);
}
*/

void BoxesFromBitmap(const cv::Mat pred, const cv::Mat bitmap, 
	const int& final_resize_h, const int& final_resize_w, 
	st_encrypt_info& info, st_detect_unit& unit);

std::vector<std::vector<float>> GetMiniBoxes(cv::RotatedRect box, float& ssid);
std::vector<std::vector<float>> Mat2Vector(cv::Mat mat);
bool XsortFp32(std::vector<float> a, std::vector<float> b);
bool XsortInt(std::vector<int> a, std::vector<int> b);
void GetContourArea(const std::vector<std::vector<float>>& box,
	float unclip_ratio, float& distance);
cv::RotatedRect UnClip(std::vector<std::vector<float>> box,
	const float& unclip_ratio);
float PolygonScoreAcc(std::vector<cv::Point> contour,
	cv::Mat pred);
float BoxScoreFast(std::vector<std::vector<float>> box_array,
	cv::Mat pred);
std::vector<std::vector<std::vector<int>>> FilterTagDetRes(
	std::vector<std::vector<std::vector<int>>> boxes, std::vector<float> & v_box_prob, float ratio_h,
	float ratio_w, st_img_rgb& srcimg);
std::vector<std::vector<int>> OrderPointsClockwise(std::vector<std::vector<int>> pts);
cv::Mat GetRotateCropImage(const cv::Mat& srcimage,
	std::vector<std::vector<int>> box);
inline float clampf(float x, float min, float max) {
	if (x > max)
		return max;
	if (x < min)
		return min;
	return x;
}
template <class T> inline T clamp(T x, T min, T max) {
	if (x > max)
		return max;
	if (x < min)
		return min;
	return x;
}
//============================================================================


void InferSegV2::Detect(st_detect_unit* p_unit, int num)
{
	int batch_size = m_info.batch_size;
	if (num>batch_size)
	{
		std::cout << "input unit size > batch size!" << std::endl;
		num = batch_size;
	}

	auto a_start = std::chrono::high_resolution_clock::now();

	int net_w = m_info.img_max_len,
		net_h = m_info.img_max_len,
		net_c = m_info.input_channel;
	int resize_h = 0;
	int resize_w = 0;
	std::vector<float> curInput;
	std::vector<cv::Mat> v_mats;

	if (true)//cuda resize
	{
		if (_v_resize_space.size() < batch_size)
		{
			for (auto i : _v_resize_space)
			{
				CuImgResizeFree(i);
			}
			_v_resize_space.resize(batch_size);
		}
		float* dlm_input_img = (float*)m_v_dev_buffer[0];
		int input_ptr_index = 0;
		{
			float mean[3] = { m_info.img_mean[0], m_info.img_mean[1], m_info.img_mean[2] };
			float dev[3] = { m_info.img_std[0], m_info.img_std[1], m_info.img_std[2] };
			for (int i = 0; i < batch_size; ++i)
			{
				//根据输入的图像数对应图像数据，如果输入数小于batch用第一个图像数据填充
				st_detect_unit& data = i < num ? p_unit[i] : p_unit[0];
				st_img_rgb& img = data.img;

				float ratio;
				if (__max(float(img.h), float(img.w)) > m_info.img_max_len)
				{
					if (float(img.h) > float(img.w))
						ratio = float(m_info.img_max_len) / float(img.h);
					else
						ratio = float(m_info.img_max_len) / float(img.w);
				}
				else
				{
					ratio = 1.0f;
				}

				resize_h = int(float(img.h) * ratio);
				resize_w = int(float(img.w) * ratio);

				resize_h = __max(int(std::round(float(resize_h) / 32) * 32), 32);
				resize_w = __max(int(std::round(float(resize_w) / 32) * 32), 32);

				st_cuda_resize_dev_space& space = _v_resize_space[i];

				int max_line_size = __max(__max(img.w, resize_w), __max(img.h, resize_h));
				CuImgResizeMalloc(space, max_line_size * max_line_size);

				space.img_rgbf_resized = dlm_input_img + input_ptr_index;
				if (img.pp_rgb8[0])
				{
					std::swap(img.pp_rgb8[0], img.pp_rgb8[2]);
					CuImgResizeRgb8(img.pp_rgb8, img.w, img.h, space, resize_w, resize_h, mean, dev);
					std::swap(img.pp_rgb8[0], img.pp_rgb8[2]);
				}
				else if (img.pp_rgb16[0])
				{
					std::swap(img.pp_rgb16[0], img.pp_rgb16[2]);
					CuImgResizeRgb16(img.pp_rgb16, img.w, img.h, space, resize_w, resize_h, mean, dev);
					std::swap(img.pp_rgb16[0], img.pp_rgb16[2]);
				}
				else if (img.pp_rgbf[0])
				{
					std::swap(img.pp_rgbf[0], img.pp_rgbf[2]);
					CuImgResizeRgbF(img.pp_rgbf, img.w, img.h, space, resize_w, resize_h, mean, dev);
					std::swap(img.pp_rgbf[0], img.pp_rgbf[2]);
				}
				input_ptr_index += i * resize_h * resize_w * net_c;
			}
		}
		if (false)
		{
			static int test_count = 0;
			{
				using namespace cv;
				std::vector<float> v_img;
				v_img.resize(resize_w * resize_h * 3);
				cudaMemcpy(v_img.data(), m_v_dev_buffer[0], resize_w * resize_h * 3 * sizeof(float), cudaMemcpyDeviceToHost);
				Mat mat;
				mat.create(resize_h, resize_w, CV_8UC3);
				int img_size = mat.rows * mat.cols;
				int index = 0;
				for (int i = 0; i < mat.rows; ++i)
				{
					for (int j = 0; j < mat.cols; ++j)
					{
						Vec3b rgb;
						rgb[2] = (v_img[index]) * 255.f;
						rgb[1] = (v_img[index + img_size]) * 255.f;
						rgb[0] = (v_img[index + img_size + img_size]) * 255.f;
						mat.at<Vec3b>(i, j) = rgb;
						index++;
					}
				}
				test_count++;
				imwrite("resize_" + std::to_string(test_count) + ".png", mat);
			}
		}
	}
	else//cv resize
	{ 
 		std::tie(curInput, resize_h, resize_w) = prepareImage(p_unit, num, v_mats);
		cudaMemcpy(m_v_dev_buffer[0], curInput.data(), curInput.size() * sizeof(float), cudaMemcpyHostToDevice);
	}
	//buffer[0]
	{
		int inputIndex = 0;
		m_context->setOptimizationProfile(0);
		auto in_dims = m_context->getBindingDimensions(inputIndex);
		in_dims.d[0] = m_info.batch_size;
		in_dims.d[1] = 3;
		in_dims.d[2] = resize_h;
		in_dims.d[3] = resize_w;
		m_context->setBindingDimensions(inputIndex, in_dims);
	}
	auto a_end = std::chrono::high_resolution_clock::now();
	float a_total_inf = std::chrono::duration<float, std::milli>(a_end - a_start).count();
	std::cout << "PreProcess take: " << a_total_inf << " ms." << std::endl;
	// buffer[1]
	{
		int outputIndex = 1;
		auto out_dims = m_context->getBindingDimensions(outputIndex);
		int outSize = 1;
		for (int j = 0; j < out_dims.nbDims; j++)
		{
			outSize *= out_dims.d[j];
		}
	}
	// do inference
	{
		auto t_start = std::chrono::high_resolution_clock::now();
		m_context->executeV2(m_v_dev_buffer.data());
		auto t_end = std::chrono::high_resolution_clock::now();
		float total_inf = std::chrono::duration<float, std::milli>(t_end - t_start).count();
		std::cout << "Inference take: " << total_inf << " ms." << std::endl;
	}
	//postProcess
	{
		int out_buf_size = resize_h * resize_w * sizeof(float);
		if (m_v_out_buf.size() < out_buf_size / sizeof(float))
		{
			m_v_out_buf.resize(out_buf_size / sizeof(float));
		}
		cudaMemcpy(m_v_out_buf.data(), m_v_dev_buffer[1], out_buf_size, cudaMemcpyDeviceToHost);
		
		auto p_start = std::chrono::high_resolution_clock::now();
 		postProcess(p_unit, num,m_v_out_buf.data(), resize_h, resize_w);
 		auto p_end = std::chrono::high_resolution_clock::now();
 		float total_inf = std::chrono::duration<float, std::milli>(p_end - p_start).count();
 		std::cout << "postProcess take: " << total_inf << " ms." << std::endl;
	}
// 	st_result_info result =	p_unit[0].result;
// 	{
// 		for (auto i = 0; i < result.vaild_num; ++i)
// 		{
// 			//cv::Mat Rotated_img;
// 			//Rotated_img = GetRotateCropImage(src_img, box);
// 			//cv::imshow("Box", Rotated_img);
// 			//cv::waitKey(0);
// 			auto box = boxes[i];
// 			auto& ploygon = result.boxes[i].polygon;
// 			if (box.size() > POLY_NODE_NUM)
// 			{
// 				std::cout << "polygon node num > POLY_NODE_NUM" << std::endl;
// 			}
// 			std::cout << "ploygon id: i: " << i << std::endl;
// 			ploygon.node_num = __min(box.size(), POLY_NODE_NUM);
// 			for (auto j = 0; j < ploygon.node_num; ++j)
// 			{
// 				ploygon.pts[j].x = box[j][0];
// 				ploygon.pts[j].y = box[j][1];
// 				std::cout << " node id :" << j << " x: " << box[j][0] << " y: " << box[j][1]<<std::endl;
// 				//cv::Point pt1(box[i][0], box[i][1]);
// 				//cv::Point pt2(box[(i + 1) % 4][0], box[(i + 1) % 4][1]);
// 				//cv::line(src_img, pt1, pt2, cv::Scalar(0, 255, 0), 2); // ��ɫ�߶�
// 			}
// 		}
// 	}
	//cv::imshow("Boxes", src_img);
 	//cv::waitKey(0);
	//return boxes;
}

void InferSegV2::postProcess(st_detect_unit* p_unit,int size, float* output,const int& final_resize_h, const int& final_resize_w)
{
	st_detect_unit& unit = p_unit[0];
	
	st_img_rgb & img = unit.img;
	cv::Mat src_img;
	{
		src_img.create(img.h, img.w, CV_8UC3);
		int index = 0;
		for (int i = 0; i < src_img.rows; ++i)
		{
			for (int j = 0; j < src_img.cols; ++j)
			{
				cv::Vec3b rgb;
				rgb[2] = img.pp_rgbf[0][index] * 255.f;
				rgb[1] = img.pp_rgbf[1][index] * 255.f;
				rgb[0] = img.pp_rgbf[2][index] * 255.f;
				src_img.at<cv::Vec3b>(i, j) = rgb;
				index++;
			}
		}
	}

	std::vector<float> pred(final_resize_h * final_resize_w, 0.0);
	std::vector<unsigned char> cbuf(final_resize_h * final_resize_w, ' ');

	for (int i = 0; i < final_resize_h * final_resize_w; i++)
	{
		pred[i] = float(output[i]);
		cbuf[i] = (unsigned char)((output[i]) * 255);
	}

	cv::Mat cbuf_map(final_resize_h, final_resize_w, CV_8UC1, (unsigned char*)cbuf.data());
	cv::Mat pred_map(final_resize_h, final_resize_w, CV_32F, (float*)pred.data());

	const double threshold = 0.3 * 255;
	const double maxvalue = 255;
	cv::Mat bit_map;
	cv::threshold(cbuf_map, bit_map, threshold, maxvalue, cv::THRESH_BINARY);

	if (m_info.dilation)
	{
		cv::Mat dila_ele =
			cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2, 2));
		cv::dilate(bit_map, bit_map, dila_ele);
	}
	auto p_start = std::chrono::high_resolution_clock::now();

	//cv::imshow("bit_map", bit_map);
	//cv::waitKey(0);
	BoxesFromBitmap(pred_map, bit_map, final_resize_h, final_resize_w, m_info, unit);

	auto p_end = std::chrono::high_resolution_clock::now();
	float total_inf = std::chrono::duration<float, std::milli>(p_end - p_start).count();
	std::cout << "BoxesFromBitmap take: " << total_inf << " ms." << std::endl;
}
void BoxesFromBitmap(
	const cv::Mat pred, const cv::Mat bitmap,const int& final_resize_h, const int& final_resize_w,st_encrypt_info & info, st_detect_unit & unit) 
{
	const int min_size = 3;
	const int max_candidates = 1000;

	int width = bitmap.cols;
	int height = bitmap.rows;

	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;

	cv::findContours(bitmap, contours, hierarchy, cv::RETR_LIST,
		cv::CHAIN_APPROX_SIMPLE);

	int num_contours =
		contours.size() >= max_candidates ? max_candidates : contours.size();

	std::string det_db_score_mode = info.mode;

	std::vector<std::vector<std::vector<int>>> boxes;
	std::vector<float> v_box_prob;

	for (int _i = 0; _i < num_contours; _i++) {
		
		if (contours[_i].size() <= 2) {
			continue;
		}
		float ssid;
		cv::RotatedRect box = cv::minAreaRect(contours[_i]);
		auto array = GetMiniBoxes(box, ssid);

		auto box_for_unclip = array;
		// end get_mini_box

		if (ssid < min_size) {
			continue;
		}
		float score;
		if (det_db_score_mode == "slow")
			/* compute using polygon*/
			score = PolygonScoreAcc(contours[_i], pred);
		else
			score = BoxScoreFast(array, pred);

// 		if (score < info.box_threshold)
// 			continue;
		if (score < unit.param.prob_thres)
			continue;

		// start for unclip
		cv::RotatedRect points = UnClip(box_for_unclip, info.unclip_ratio);
		if (points.size.height < 1.001 && points.size.width < 1.001) {
			continue;
		}
		// end for unclip

		cv::RotatedRect clipbox = points;
		auto cliparray = GetMiniBoxes(clipbox, ssid);

		if (ssid < min_size + 2)
			continue;

		int dest_width = pred.cols;
		int dest_height = pred.rows;
		std::vector<std::vector<int>> intcliparray;
		for (int num_pt = 0; num_pt < 4; num_pt++)
		{
			std::vector<int> a{ int(clampf(roundf(cliparray[num_pt][0] / float(width) *
												 float(dest_width)),
										  0, float(dest_width))),
							   int(clampf(roundf(cliparray[num_pt][1] /
												 float(height) * float(dest_height)),
										  0, float(dest_height))) };
			intcliparray.push_back(a);
		}
		boxes.push_back(intcliparray);
		v_box_prob.push_back(score);

	} // end for
	
	float ratio_h = float(final_resize_h) / float(unit.img.h);
	float ratio_w = float(final_resize_w) / float(unit.img.w);
	boxes = FilterTagDetRes(boxes, v_box_prob, ratio_h, ratio_w, unit.img);

	for (auto i = 0; i < v_box_prob.size(); ++i)
	{
		if (i >= BOX_MAX_NUM)
		{
			break;
		}
		//std::cout << "ploygon id: i: " << i << std::endl;

		st_box& box = unit.result.boxes[i];
		box.prob = v_box_prob[i];
		box.polygon.node_num = 4;
		
		unit.result.vaild_num = i + 1;

		for (int num_pt = 0; num_pt < 4; num_pt++)
		{
			//std::cout << " node id :" << num_pt << " x: " << boxes[i][num_pt][0] << " y: " << boxes[i][num_pt][1] << std::endl;

			box.polygon.pts[num_pt].x = boxes[i][num_pt][0];
			box.polygon.pts[num_pt].y = boxes[i][num_pt][1];
		}
	}
}

std::vector<std::vector<float>> GetMiniBoxes(cv::RotatedRect box, float& ssid) {
	ssid = __max(box.size.width, box.size.height);

	cv::Mat points;
	cv::boxPoints(box, points);

	auto array = Mat2Vector(points);
	std::sort(array.begin(), array.end(), XsortFp32);

	std::vector<float> idx1 = array[0], idx2 = array[1], idx3 = array[2],
		idx4 = array[3];
	if (array[3][1] <= array[2][1]) {
		idx2 = array[3];
		idx3 = array[2];
	}
	else {
		idx2 = array[2];
		idx3 = array[3];
	}
	if (array[1][1] <= array[0][1]) {
		idx1 = array[1];
		idx4 = array[0];
	}
	else {
		idx1 = array[0];
		idx4 = array[1];
	}

	array[0] = idx1;
	array[1] = idx2;
	array[2] = idx3;
	array[3] = idx4;

	return array;
}

std::vector<std::vector<float>> Mat2Vector(cv::Mat mat) {
	std::vector<std::vector<float>> img_vec;
	std::vector<float> tmp;

	for (int i = 0; i < mat.rows; ++i) {
		tmp.clear();
		for (int j = 0; j < mat.cols; ++j) {
			tmp.push_back(mat.at<float>(i, j));
		}
		img_vec.push_back(tmp);
	}
	return img_vec;
}

bool XsortFp32(std::vector<float> a, std::vector<float> b) {
	if (a[0] != b[0])
		return a[0] < b[0];
	return false;
}

bool XsortInt(std::vector<int> a, std::vector<int> b) {
	if (a[0] != b[0])
		return a[0] < b[0];
	return false;
}


void GetContourArea(const std::vector<std::vector<float>>& box,
	float unclip_ratio, float& distance) {
	int pts_num = 4;
	float area = 0.0f;
	float dist = 0.0f;
	for (int i = 0; i < pts_num; i++) {
		area += box[i][0] * box[(i + 1) % pts_num][1] -
			box[i][1] * box[(i + 1) % pts_num][0];
		dist += sqrtf((box[i][0] - box[(i + 1) % pts_num][0]) *
			(box[i][0] - box[(i + 1) % pts_num][0]) +
			(box[i][1] - box[(i + 1) % pts_num][1]) *
			(box[i][1] - box[(i + 1) % pts_num][1]));
	}
	area = fabs(float(area / 2.0));

	distance = area * unclip_ratio / dist;
}

cv::RotatedRect UnClip(std::vector<std::vector<float>> box,
	const float& unclip_ratio) {
	float distance = 1.0;

	GetContourArea(box, unclip_ratio, distance);

	ClipperLib::ClipperOffset offset;
	ClipperLib::Path p;
	p << ClipperLib::IntPoint(int(box[0][0]), int(box[0][1]))
		<< ClipperLib::IntPoint(int(box[1][0]), int(box[1][1]))
		<< ClipperLib::IntPoint(int(box[2][0]), int(box[2][1]))
		<< ClipperLib::IntPoint(int(box[3][0]), int(box[3][1]));
	offset.AddPath(p, ClipperLib::jtRound, ClipperLib::etClosedPolygon);

	ClipperLib::Paths soln;
	offset.Execute(soln, distance);
	std::vector<cv::Point2f> points;

	for (int j = 0; j < soln.size(); j++) {
		for (int i = 0; i < soln[soln.size() - 1].size(); i++) {
			points.emplace_back(soln[j][i].X, soln[j][i].Y);
		}
	}
	cv::RotatedRect res;
	if (points.size() <= 0) {
		res = cv::RotatedRect(cv::Point2f(0, 0), cv::Size2f(1, 1), 0);
	}
	else {
		res = cv::minAreaRect(points);
	}
	return res;
}

float PolygonScoreAcc(std::vector<cv::Point> contour,
	cv::Mat pred) {
	int width = pred.cols;
	int height = pred.rows;
	std::vector<float> box_x;
	std::vector<float> box_y;
	for (int i = 0; i < contour.size(); ++i) {
		box_x.push_back(contour[i].x);
		box_y.push_back(contour[i].y);
	}

	int xmin =
		clamp(int(std::floor(*(std::min_element(box_x.begin(), box_x.end())))), 0,
			width - 1);
	int xmax =
		clamp(int(std::ceil(*(std::max_element(box_x.begin(), box_x.end())))), 0,
			width - 1);
	int ymin =
		clamp(int(std::floor(*(std::min_element(box_y.begin(), box_y.end())))), 0,
			height - 1);
	int ymax =
		clamp(int(std::ceil(*(std::max_element(box_y.begin(), box_y.end())))), 0,
			height - 1);

	cv::Mat mask;
	mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1);

	cv::Point* rook_point = new cv::Point[contour.size()];

	for (int i = 0; i < contour.size(); ++i) {
		rook_point[i] = cv::Point(int(box_x[i]) - xmin, int(box_y[i]) - ymin);
	}
	const cv::Point* ppt[1] = { rook_point };
	int npt[] = { int(contour.size()) };

	cv::fillPoly(mask, ppt, npt, 1, cv::Scalar(1));

	cv::Mat croppedImg;
	pred(cv::Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1))
		.copyTo(croppedImg);
	float score = cv::mean(croppedImg, mask)[0];

	delete[] rook_point;
	return score;
}

float BoxScoreFast(std::vector<std::vector<float>> box_array,
	cv::Mat pred) {
	auto array = box_array;
	int width = pred.cols;
	int height = pred.rows;

	float box_x[4] = { array[0][0], array[1][0], array[2][0], array[3][0] };
	float box_y[4] = { array[0][1], array[1][1], array[2][1], array[3][1] };

	int xmin = clamp(int(std::floor(*(std::min_element(box_x, box_x + 4)))), 0,
		width - 1);
	int xmax = clamp(int(std::ceil(*(std::max_element(box_x, box_x + 4)))), 0,
		width - 1);
	int ymin = clamp(int(std::floor(*(std::min_element(box_y, box_y + 4)))), 0,
		height - 1);
	int ymax = clamp(int(std::ceil(*(std::max_element(box_y, box_y + 4)))), 0,
		height - 1);

	cv::Mat mask;
	mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1);

	cv::Point root_point[4];
	root_point[0] = cv::Point(int(array[0][0]) - xmin, int(array[0][1]) - ymin);
	root_point[1] = cv::Point(int(array[1][0]) - xmin, int(array[1][1]) - ymin);
	root_point[2] = cv::Point(int(array[2][0]) - xmin, int(array[2][1]) - ymin);
	root_point[3] = cv::Point(int(array[3][0]) - xmin, int(array[3][1]) - ymin);
	const cv::Point* ppt[1] = { root_point };
	int npt[] = { 4 };
	cv::fillPoly(mask, ppt, npt, 1, cv::Scalar(1));

	cv::Mat croppedImg;
	pred(cv::Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1))
		.copyTo(croppedImg);

	auto score = cv::mean(croppedImg, mask)[0];
	return score;
}

std::vector<std::vector<std::vector<int>>> FilterTagDetRes(
	std::vector<std::vector<std::vector<int>>> boxes, std::vector<float> & v_box_prob, float ratio_h,
	float ratio_w, st_img_rgb & srcimg) {
	int oriimg_h = srcimg.h;
	int oriimg_w = srcimg.w;

	std::vector<std::vector<std::vector<int>>> root_points;
	std::vector<float> _v_box_prob;

	for (int n = 0; n < boxes.size(); n++) {
		boxes[n] = OrderPointsClockwise(boxes[n]);
		for (int m = 0; m < boxes[0].size(); m++) {
			boxes[n][m][0] /= ratio_w;
			boxes[n][m][1] /= ratio_h;

			boxes[n][m][0] = int(__min(__max(boxes[n][m][0], 0), oriimg_w - 1));
			boxes[n][m][1] = int(__min(__max(boxes[n][m][1], 0), oriimg_h - 1));
		}
	}

	for (int n = 0; n < boxes.size(); n++) {
		int rect_width, rect_height;
		rect_width = int(sqrt(pow(boxes[n][0][0] - boxes[n][1][0], 2) +
			pow(boxes[n][0][1] - boxes[n][1][1], 2)));
		rect_height = int(sqrt(pow(boxes[n][0][0] - boxes[n][3][0], 2) +
			pow(boxes[n][0][1] - boxes[n][3][1], 2)));
		if (rect_width <= 4 || rect_height <= 4)
			continue;
		root_points.push_back(boxes[n]);
		_v_box_prob.push_back(v_box_prob[n]);
	}
	v_box_prob = _v_box_prob;
	return root_points;
}

std::vector<std::vector<int>> OrderPointsClockwise(std::vector<std::vector<int>> pts) {
	std::vector<std::vector<int>> box = pts;
	std::sort(box.begin(), box.end(), XsortInt);

	std::vector<std::vector<int>> leftmost = { box[0], box[1] };
	std::vector<std::vector<int>> rightmost = { box[2], box[3] };

	if (leftmost[0][1] > leftmost[1][1])
		std::swap(leftmost[0], leftmost[1]);

	if (rightmost[0][1] > rightmost[1][1])
		std::swap(rightmost[0], rightmost[1]);

	std::vector<std::vector<int>> rect = { leftmost[0], rightmost[0], rightmost[1],
										  leftmost[1] };
	return rect;
}


cv::Mat GetRotateCropImage(const cv::Mat& srcimage,
	std::vector<std::vector<int>> box) {
	cv::Mat image;
	srcimage.copyTo(image);
	std::vector<std::vector<int>> points = box;

	int x_collect[4] = { box[0][0], box[1][0], box[2][0], box[3][0] };
	int y_collect[4] = { box[0][1], box[1][1], box[2][1], box[3][1] };
	int left = int(*std::min_element(x_collect, x_collect + 4));
	int right = int(*std::max_element(x_collect, x_collect + 4));
	int top = int(*std::min_element(y_collect, y_collect + 4));
	int bottom = int(*std::max_element(y_collect, y_collect + 4));

	cv::Mat img_crop;
	image(cv::Rect(left, top, right - left, bottom - top)).copyTo(img_crop);

	for (int i = 0; i < points.size(); i++) {
		points[i][0] -= left;
		points[i][1] -= top;
	}

	int img_crop_width = int(sqrt(pow(points[0][0] - points[1][0], 2) +
		pow(points[0][1] - points[1][1], 2)));
	int img_crop_height = int(sqrt(pow(points[0][0] - points[3][0], 2) +
		pow(points[0][1] - points[3][1], 2)));

	cv::Point2f pts_std[4];
	pts_std[0] = cv::Point2f(0., 0.);
	pts_std[1] = cv::Point2f(img_crop_width, 0.);
	pts_std[2] = cv::Point2f(img_crop_width, img_crop_height);
	pts_std[3] = cv::Point2f(0.f, img_crop_height);

	cv::Point2f pointsf[4];
	pointsf[0] = cv::Point2f(points[0][0], points[0][1]);
	pointsf[1] = cv::Point2f(points[1][0], points[1][1]);
	pointsf[2] = cv::Point2f(points[2][0], points[2][1]);
	pointsf[3] = cv::Point2f(points[3][0], points[3][1]);

	cv::Mat M = cv::getPerspectiveTransform(pointsf, pts_std);

	cv::Mat dst_img;
	cv::warpPerspective(img_crop, dst_img, M,
		cv::Size(img_crop_width, img_crop_height),
		cv::BORDER_REPLICATE);

	if (float(dst_img.rows) >= float(dst_img.cols) * 1.5) {
		cv::Mat srcCopy = cv::Mat(dst_img.rows, dst_img.cols, dst_img.depth());
		cv::transpose(dst_img, srcCopy);
		cv::flip(srcCopy, srcCopy, 0);
		return srcCopy;
	}
	else {
		return dst_img;
	}
}
}//txr_algo_dlm_seg